package cn.jingyuan.bee.utils.map;

import cn.jingyuan.bee.utils.ArrayUtils;
import cn.jingyuan.bee.utils.BooleanUtils;
import cn.jingyuan.bee.utils.ClassLoaderUtils;
import cn.jingyuan.bee.utils.StringUtils;
import cn.jingyuan.bee.utils.convert.Convert;
import cn.jingyuan.bee.utils.getter.OptNullBasicTypeFromObjectGetter;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.Map;
import java.util.Set;

/**
 * Map 代理，提供各种 getXXX 方法，并提供默认值支持
 */
public class MapProxy implements Map<Object, Object>, OptNullBasicTypeFromObjectGetter<Object>, InvocationHandler, Serializable {

    Map map;

    /**
     * 构造
     *
     * @param map 被代理的 Map
     */
    public MapProxy(Map<?, ?> map) {
        this.map = map;
    }

    /**
     * 创建代理 Map<br>
     * 此类对 Map 做一次包装，提供各种 getXXX 方法
     *
     * @param map 被代理的 Map
     *
     * @return {@link MapProxy}
     */
    public static MapProxy create(Map<?, ?> map) {
        return (map instanceof MapProxy) ? (MapProxy) map : new MapProxy(map);
    }

    @Override
    public Object getObject(Object key, Object defaultValue) {
        final Object value = map.get(key);
        return null != value ? value : defaultValue;
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public boolean isEmpty() {
        return map.isEmpty();
    }

    @Override
    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return map.containsValue(value);
    }

    @Override
    public Object get(Object key) {
        return map.get(key);
    }

    @Override
    public Object put(Object key, Object value) {
        return map.put(key, value);
    }

    @Override
    public Object remove(Object key) {
        return map.remove(key);
    }

    @Override
    public void putAll(Map<?, ?> m) {
        map.putAll(m);
    }

    @Override
    public void clear() {
        map.clear();
    }

    @Override
    public Set<Object> keySet() {
        return map.keySet();
    }

    @Override
    public Collection<Object> values() {
        return map.values();
    }

    @Override
    public Set<Entry<Object, Object>> entrySet() {
        return map.entrySet();
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        final Class<?>[] parameterTypes = method.getParameterTypes();
        if (ArrayUtils.isEmpty(parameterTypes)) {
            final Class<?> returnType = method.getReturnType();
            if (null != returnType && void.class != returnType) {
                // 匹配 Getter
                final String methodName = method.getName();
                String fieldName = null;
                if (methodName.startsWith("get")) {
                    // 匹配 getXXX
                    fieldName = StringUtils.removePreAndLowerFirst(methodName, 3);
                } else if (BooleanUtils.isBoolean(returnType) && methodName.startsWith("is")) {
                    // 匹配 isXXX
                    fieldName = StringUtils.removePreAndLowerFirst(methodName, 2);
                } else if ("hashCode".equals(methodName)) {
                    return this.hashCode();
                } else if ("toString".equals(methodName)) {
                    return this.toString();
                }

                if (StringUtils.isNotBlank(fieldName)) {
                    if (!this.containsKey(fieldName)) {
                        // 驼峰不存在转下划线尝试
                        fieldName = StringUtils.toUnderlineCase(fieldName);
                    }
                    return Convert.convert(method.getGenericReturnType(), this.get(fieldName));
                }
            }

        } else if (1 == parameterTypes.length) {
            // 匹配 Setter
            final String methodName = method.getName();
            if (methodName.startsWith("set")) {
                final String fieldName = StringUtils.removePreAndLowerFirst(methodName, 3);
                if (StringUtils.isNotBlank(fieldName)) {
                    this.put(fieldName, args[0]);
                }
            } else if ("equals".equals(methodName)) {
                return this.equals(args[0]);
            }
        }

        throw new UnsupportedOperationException(method.toGenericString());
    }

    /**
     * 将 Map 代理为指定接口的动态代理对象
     *
     * @param <T> 代理的 Bean 类型
     * @param interfaceClass 接口
     *
     * @return 代理对象
     */
    public <T> T toProxyBean(Class<T> interfaceClass) {
        return (T) Proxy.newProxyInstance(ClassLoaderUtils.getClassLoader(), new Class<?>[]{interfaceClass}, this);
    }

}
